using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System;

// section is base part of rythem.
// section contains several events, each events fire somting action,such as sing, shutup or playerinputtest.
// section plays one after another.
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public class AnimEventTmpl
{
	public string stringParameter;
	public string functionName;
	public float time;
}

public class SectionTmpl : ICloneable
{
	private List<AnimEventTmpl> evtList;
	public List<AnimEventTmpl> Events
	{
		set {evtList = value;}
		get {return evtList;}
	}

	private int id;
	public int ID
	{
		set {id = value;}
		get {return id;}
	}

	public SectionTmpl()
	{
		evtList = new List<AnimEventTmpl> ();
	}
	
	public void AddEvent(AnimEventTmpl evt)
	{
		evtList.Add(evt);
	}

	public object Clone()
	{
		SectionTmpl newTmpl = new SectionTmpl ();
		newTmpl.Events = new List<AnimEventTmpl> (Events);
		return newTmpl as object;
	}
}

public class Section
{
	public SectionTmpl tmpl;

	private int notetime;
	public int Notetime
	{
		set {notetime = value;}
		get {return notetime;}
	}
}


public class Rythem : MonoBehaviour {
	protected List<Section> secList;
	public string levelclass;
	private float timeline;
	private float meter;
	public float Meter
	{
		set {meter = value;}
		get {return meter;}
	}

	public float Timeline
	{
		get {return timeline;}
	}

	// Use this for initialization
	void Start () {

	}

	public void SetMeterPerMin(int mpm)
	{
		meter = mpm;
	}

	public void AddSection(Section sec)
	{
		if(null == secList){
			secList = new List<Section> ();
		}
		secList.Add (sec);
	}

	// fireevent translate funciton from to string type to runable type,then fire it
	// for example "play1sing System.INT32 1" -> "void play1sing(1)"
	void FireEvent(AnimEventTmpl evt)
	{
		Component cp = GetComponent(levelclass);
		if(null == cp)
		{
			Debug.LogError ("Can't find instance of type := " + levelclass + " put rythem.cs together");
		}

		MethodInfo mi = cp.GetType().GetMethod(evt.functionName);
		
		ParameterInfo[] pmInfo = mi.GetParameters();

		if (pmInfo.Length == 0) {
			mi.Invoke(cp, null);
			return;
		}

		foreach(ParameterInfo pm in pmInfo)
		{
			string[] spType = evt.stringParameter.Split(':');

			if (pm.ParameterType != Type.GetType (spType [0]))
			{
				return;
			}

			if ("Int32" == pm.ParameterType.Name)
			{
				int i = System.Int32.Parse (spType[1]);
				mi.Invoke(cp, new object[]{i});
			}
			else if("Single" == pm.ParameterType.Name)
			{
				float f = System.Single.Parse(spType[1]);
				mi.Invoke(cp, new object[]{f});
			}
			else if("String" == pm.ParameterType.Name)
			{
				Debug.Log(pm.ParameterType.ToString());
			}
			else
			{
				mi.Invoke(cp, new object[]{Enum.Parse(Type.GetType(spType [0]), spType[1])});
			}
		}
	}

	// Update is called once per frame
	void Update () {

	}

	// fixupdate use to check if it is time to fireevent in a section or time to move to next section.
	void FixedUpdate(){
		if (null == secList)
			return;

		if (secList.Count == 0)
			return;

		float processTime = Time.time - timeline;
		if (processTime >= secList [0].Notetime * this.Meter) 
		{
			OnEnterSection(secList [0]);
		}

		// if section contains event
		if (null != secList [0].tmpl)
		{
			List<AnimEventTmpl> events = secList [0].tmpl.Events;
			
			for(int i = events.Count - 1; i >= 0; --i)
			{
				AnimEventTmpl ae = events[i];
				
				if(processTime >= secList [0].Notetime * this.Meter + ae.time)
				{
					FireEvent(ae);
					events.RemoveAt(i);
					break;
				}
			}
		}

		// check if section overtime
		if (secList.Count > 1)
		{
			float nextSec = secList[1].Notetime * this.Meter;

			if(processTime >= nextSec)
			{
				OnLeaveSection(secList[0]);
				secList.RemoveAt(0);
			}
		}
	}

	public void OnEnterSection (Section sec)
	{

	}

	public void OnLeaveSection (Section sec)
	{

	}

	public void TimeStart()
	{
		timeline = Time.time;
	}
}